package com.winning.pmph.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.winning.pmph.entity.*;
import com.winning.pmph.mapper.BookCategoryMapper;
import com.winning.pmph.service.TagService;
import com.winning.pmph.vo.ExamBookCatalog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.ParseSettings;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;

import javax.imageio.ImageIO;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
public class ExamBookUtil {

    public static TagService tagService = ApplicationContextUtil.getBean(TagService.class);
    public static BookCategoryMapper bookCategoryMapper = ApplicationContextUtil.getBean(BookCategoryMapper.class);
    public static Map<String, Integer> importBookPercentageMap = new ConcurrentHashMap<>();

    public static String copyPicAndTable(String path, ExamBook book) {
        try {
//            picsDirPath = bookSourceFile.getAnalysisPath() + "/pics";
            // 把图书里的图片统一存储到这个路径下
            String targetDirPath = ApplicationContextUtil.BASE_DOWN_FORDER + "image/examBook/" + book.getIsbn();
            FileUtils.deleteDirectory(new File(targetDirPath));
            FileUtils.copyDirectory(new File(path), new File(targetDirPath));
            // 维护静态资源表
            //saveBookFileLib(targetDirPath, book);
            return targetDirPath + "/pics";
        } catch (Exception e) {
            log.error("移动图片文件夹异常：", e);
            throw new RuntimeException("移动图片文件夹异常：" + e.toString());
        }
    }

    public static String unzipBook(BookSourceFile bookSourceFile, String header) {
        // 先删除原解压文件;
        String unzipPath = StringUtils.removeEnd(bookSourceFile.getPath(), ".zip");
        try {
            FileUtils.deleteDirectory(new File(unzipPath));
            ZipUtil.unZip(bookSourceFile.getPath(), header);
        } catch (Exception e) {
            log.error("解压图书失败：", e);
            throw new RuntimeException("解压图书失败：" + e.toString());
        }
        return unzipPath;
    }

    public static ExamBook parseBook(ExamBook examBook) {
        ExamBook book = null;
        // 解析元数据
        JSONObject metadataJson = parseMetadataXml2Json(examBook.getAnalysisPath());
        String isbn = metadataJson.getString("ISBN");
        String bookName = metadataJson.getJSONObject("Titles").getString("Main_Title");
        FileInputStream stream = null;
        try {
            File file = new File(examBook.getAnalysisPath() + "/book.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(ExamBook.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            stream = new FileInputStream(file);
            book = (ExamBook) unmarshaller.unmarshal(stream);
            // 设置图书部分元数据信息;
            book.setId(examBook.getId());
            book.setName(bookName);
            book.setIsbn(isbn);
            book.setMetadata(metadataJson);
            book.setStatus(Const.IMPORT_STATUS_SUCCESS);
            // 图书文章, 转dom;
            if (CollectionUtils.isNotEmpty(book.getFrontArticleList())) {
                book.getFrontArticleList().forEach(article -> article.setUnitContentDom(Jsoup.parse(article.getUnitContent())));
            }
            book.getBodyArticleList().forEach(article -> article.setUnitContentDom(Jsoup.parse(article.getUnitContent())));
            if (CollectionUtils.isNotEmpty(book.getBackArticleList())) {
                book.getBackArticleList().forEach(article -> article.setUnitContentDom(Jsoup.parse(article.getUnitContent())));
            }
        } catch (Exception e) {
            log.error("解析图书时发生错误：", e);
            try {
                if (null != stream) {
                    stream.close();
                }
            } catch (IOException i) {
                i.printStackTrace();
            }
            throw new RuntimeException("解析图书时发生错误：" + e.toString());
        }
        return book;
    }

    public static void parsePicAndTable(ExamBook book, File picsDir) {
        FileInputStream imgStream = null;
        try {
            Unmarshaller picUnmarshaller = JAXBContext.newInstance(ExamBookPic.class).createUnmarshaller();
            Unmarshaller tableUnmarshaller = JAXBContext.newInstance(ExamBookTable.class).createUnmarshaller();
            for (File listFile : picsDir.listFiles()) {
                if (listFile.getName().endsWith(".xml")) {
                    imgStream = new FileInputStream(listFile);
                    // talbe.xml
                    if (listFile.getName().startsWith("T")) {
                        book.getBookTableList().add((ExamBookTable) tableUnmarshaller.unmarshal(imgStream));
                    } else if (listFile.getName().startsWith("P")) {
                        // pic.xml
                        book.getBookPicList().add((ExamBookPic) picUnmarshaller.unmarshal(imgStream));
                    }
                }
                // 图片类型;
                else {
                    book.getPictureSet().add(listFile.getName());
                }
            }
        } catch (Exception e) {
            if (null != imgStream) {
                try {
                    imgStream.close();
                } catch (IOException i) {
                    i.printStackTrace();
                }
            }
            log.error("解析图片xml失败：", e);
            throw new RuntimeException("解析图片xml失败：" + e.toString());
        }
    }

    public static void bindImgSrc(ExamBook book, ExamBookArticle bookArticle, String picsTargetDirPath, String situation) {
        // 校验标题中是否含有图片
        Element titleBody = Jsoup.parse(bookArticle.getTitle()).body();
        Elements titleImgEles = titleBody.getElementsByTag("img");
        titleImgEles.forEach(imgItem -> {
            // 兼容图片ID属性可能包含图片后缀名
            String picName = StringUtils.defaultIfBlank(imgItem.attr("src"), imgItem.attr("id"));
            if (StringUtils.isBlank(picName)) {
                throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用生僻字路径为空");
            }
            // 自动添加后缀,默认添加.jpg
            picName = picName.contains(".") ? picName : (picName + ".jpg");
            picName = picName.substring(picName.lastIndexOf("/") + 1);
            if (!book.getPictureSet().contains(picName)) {
                throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用生僻字:" + picName + "不存在");
            }
            imgItem.attr("src", picsTargetDirPath + picName);
        });
        bookArticle.setTitle(titleBody.html());

        // 校验article内容的图片
        Document contentDocument = Jsoup.parse(bookArticle.getUnitContent().replaceAll("<Table", "<IMG"));
        Elements imgElements = contentDocument.getElementsByTag("img");
        imgElements.forEach(ele -> {
            String picName = "";
            if ("BZ".equals(ele.attr("class"))) {
                // 因图片路径不一致，这里做兼容处理  src="图片名称" src="pics/图片名称"
                // src属性带后缀名， id不带后缀名
                // 兼容图片ID属性可能包含图片后缀名
                picName = StringUtils.defaultIfBlank(ele.attr("src"), ele.attr("id"));
                if (StringUtils.isBlank(picName)) {
                    throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用生僻字路径为空");
                }
                // 自动添加后缀,默认添加.jpg
                picName = picName.contains(".") ? picName : (picName + ".jpg");
                picName = picName.substring(picName.lastIndexOf("/") + 1);
                if (!book.getPictureSet().contains(picName)) {
                    throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用生僻字:" + picName + "不存在");
                }
                ele.attr("src", picsTargetDirPath + picName);
            } else {
                picName = ele.attr("id");
                if (StringUtils.isBlank(picName)) {
                    throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用图片路径为空");
                }
                picName = picName.substring(picName.lastIndexOf("/") + 1) + ".jpg";
                if (!book.getPictureSet().contains(picName)) {
                    throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 引用图片:" + picName + "不存在");
                }
                ele.attr("src", picsTargetDirPath + picName);
            }
            // 文前,文后不判断
            /*if (BookArticle.SITUATION_BODY.equals(situation)) {
                // 判断非行内图是否被div包裹
                if (ele.parent().tagName().equals("body") || StringUtils.isBlank(ele.parent().attr("class"))) {
                    BufferedImage sourceImg = null;
                    InputStream murl = null;
                    try {
                        murl = new FileInputStream(new File(ApplicationContextUtil.BASE_DOWN_FORDER + "image/book/" + book.getIsbn() + "/pics/" + picName));
                        sourceImg = ImageIO.read(murl);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    int h = sourceImg.getHeight();
                    int w = sourceImg.getWidth();
                    // 根据图片宽,高判断是否是行内图
                    if (h > 100 && w > 100) {
                        throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", " +
                                "引用图片:" + picName + "未被div包裹或div标签class属性(Table/Picture)为空");
                    }
                }
            }*/
        });
        // 判断正文中是否含有4个字节的字符
        String fourBytes = "";
        try {
            fourBytes = FourBytesUtils.getFourBytes(contentDocument.body().html());
        } catch (Exception e) {
            log.error("判断图书是否含有特殊字符时报错：", e);
            throw new RuntimeException("判断图书是否含有特殊字符时报错：" + e.toString());
        }
        if (StringUtils.isNotBlank(fourBytes)) {
            throw new RuntimeException("图书: " + book.getName() + ", 内容: " + bookArticle.getTitle() + ", 含特殊字符:" + fourBytes);
        }
        bookArticle.setUnitContent(contentDocument.body().html());
    }

    public static ExamBookCatalog parseBookCatalog(String savePath) {
        ExamBookCatalog bookCatalog = null;
        FileInputStream bookCatalogStream = null;
        try {
            // 图书目录文件路径
            File bookXmlFile = new File(savePath + "/book_catalog.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(ExamBookCatalog.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            bookCatalogStream = new FileInputStream(bookXmlFile);
            bookCatalog = (ExamBookCatalog) unmarshaller.unmarshal(bookCatalogStream);
            // 设置 situation, 按照 sequence_number 去重;
            if (CollectionUtils.isNotEmpty(bookCatalog.getFrontCatalogItemList())) {
                bookCatalog.setFrontCatalogItemList(bookCatalog.getFrontCatalogItemList().stream()
                        .peek(item -> item.setSequenceNumber(item.getId()))
                        .peek(item -> item.setSituation(BookCatalogItem.SITUATION_FRONT))
                        .filter(StreamUtil.distinctByKey(ExamBookCatalogItem::getSequenceNumber))
                        .collect(Collectors.toList()));
            }
            bookCatalog.setBodyCatalogItemList(bookCatalog.getBodyCatalogItemList().stream()
                    .peek(item -> item.setSequenceNumber(item.getId()))
                    .peek(item -> item.setSituation(BookCatalogItem.SITUATION_BODY))
                    .filter(StreamUtil.distinctByKey(ExamBookCatalogItem::getSequenceNumber))
                    .collect(Collectors.toList()));
            if (CollectionUtils.isNotEmpty(bookCatalog.getBackCatalogItemList())) {
                bookCatalog.setBackCatalogItemList(bookCatalog.getBackCatalogItemList().stream()
                        .peek(item -> item.setSequenceNumber(item.getId()))
                        .peek(item -> item.setSituation(BookCatalogItem.SITUATION_BACK))
                        .filter(StreamUtil.distinctByKey(ExamBookCatalogItem::getSequenceNumber))
                        .collect(Collectors.toList()));
            }
        } catch (Exception e) {
            if (null != bookCatalogStream) {
                try {
                    bookCatalogStream.close();
                } catch (IOException i) {
                    i.printStackTrace();
                }
            }
            log.error("解析图书目录时发生错误：", e);
            throw new RuntimeException("解析图书目录时发生错误：" + e.toString());
        }
        return bookCatalog;
    }

    /**
     * 递归查询文件,
     *
     * @param dir 父文件夹
     */
    public static String recursiveFindFile(File dir) {
        if (isFound(dir)) {
            return dir.getAbsolutePath();
        }
        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                String path = recursiveFindFile(file);
                if (StringUtils.isNotBlank(path)) {
                    return path;
                }
            }
        }
        return "";
    }

    private static boolean isFound(File dir) {
        String subFileNames = StringUtils.join(dir.list(), ",");
        return subFileNames.contains("meta.xml") && subFileNames.contains("book.xml") &&
                subFileNames.contains("book_catalog.xml") && subFileNames.contains("pics");
    }

    public static JSONObject parseMetadataXml2Json(String analysisPath) {
        try {
            File metadataFile = new File(analysisPath + "/meta.xml");
            String metadataStr = FileUtils.readFileToString(metadataFile);
            Parser parser = Parser.xmlParser().settings(new ParseSettings(true, true));
            Document metadataDocument = Jsoup.parse(metadataStr, metadataFile.getPath(), parser);
            Element metadataElement = metadataDocument.getElementsByTag("book_metadata").first();
            JSONObject metadataMap = parseMetadataXml2JsonWithOrder(metadataElement, new JSONObject(true));
            String isbn = (String) metadataMap.get("ISBN");
            // 判断图书是否有封面图;
            String coverImg = Arrays.stream(new File(analysisPath).listFiles())
                    .map(File::getName)
                    .filter(fileName -> fileName.endsWith(".jpg")
                            || fileName.endsWith("jpeg")
                            || fileName.endsWith("png")
                            || fileName.endsWith("bmp"))
                    .collect(Collectors.joining(","));
            if (StringUtils.isNotBlank(coverImg)) {
                metadataMap.put("Cover_Img", coverImg);
            }
//            else {
//                throw new RuntimeException("缺少封面图。");
//            }
            return metadataMap;
        } catch (Exception e) {
            log.error("解析图书元数据时发生错误：", e);
            throw new RuntimeException("解析图书元数据时发生错误：" + e.toString());
        }
    }

    private static JSONObject parseMetadataXml2JsonWithOrder(Element element, JSONObject map) {
        for (Element child : element.children()) {
            if (child.children().size() > 0) {
                JSONObject subMap = parseMetadataXml2JsonWithOrder(child, new JSONObject(true));
                Object priSibling = map.get(child.tagName());
                if (Objects.isNull(priSibling)) {
                    if (Objects.equals("Creator", child.tagName()) || Objects.equals("Contributor", child.tagName())) {
                        JSONArray creatorArray = new JSONArray();
                        creatorArray.add(subMap);
                        map.put(child.tagName(), creatorArray);
                    } else {
                        map.put(child.tagName(), subMap);
                    }
                } else if (priSibling instanceof List) {
                    ((List) priSibling).add(subMap);
                } else {
                    List<Object> value = new ArrayList();
                    value.add(priSibling);
                    value.add(subMap);
                    map.put(child.tagName(), value);
                }
            } else {
                if (Objects.equals("Creators", child.tagName())) {
                    ArrayList<LinkedHashMap> list = new ArrayList<>();
                    Map<String, List<LinkedHashMap>> tmp = Maps.newLinkedHashMap();
                    tmp.put("Creator", list);
                    map.put("Creators", tmp);
                } else if (Objects.equals("Contributors", child.tagName())) {
                    ArrayList<LinkedHashMap> list = new ArrayList<>();
                    Map<String, List<LinkedHashMap>> tmp = Maps.newLinkedHashMap();
                    tmp.put("Contributor", list);
                    map.put("Contributors", tmp);
                } else {
                    map.put(child.tagName(), child.text());
                }
            }
        }
        return map;
    }

    /**
     * 图书图片样式, 提取素材后, 转为知识图片样式;
     *
     * @param img
     * @return
     */
    public static void parseBookImg2KnowledgeUnitImg(Element img) {
        String knowledgeUnitImgClz = getKnowledgeUnitImgClz(img);
        img.attr("class", "rw_img " + knowledgeUnitImgClz);
    }

    private static String getKnowledgeUnitImgClz(Element img) {
        String bookImgClz = img.attr("class");
        if (StringUtils.isBlank(bookImgClz)) {
            bookImgClz = img.parent().attr("class");
        }
        String id = img.attr("id");
        if (StringUtils.containsIgnoreCase(bookImgClz, "table")) return "table";
        if (StringUtils.containsIgnoreCase(bookImgClz, "general")) return "general";
        if (StringUtils.containsIgnoreCase(bookImgClz, "picture")) return "picture";
        if (StringUtils.containsIgnoreCase(bookImgClz, "cover")) return "cover";
        if (StringUtils.containsIgnoreCase(bookImgClz, "formula")) return "formula";
        if (StringUtils.containsIgnoreCase(bookImgClz, "flow")) return "flow";
        if (StringUtils.containsIgnoreCase(bookImgClz, "bz")) return "bz hnt";
        if (StringUtils.containsIgnoreCase(bookImgClz, "other")) return "other";
        if (StringUtils.containsIgnoreCase(bookImgClz, "CT")) return "picture";
        if (StringUtils.containsIgnoreCase(bookImgClz, "GS")) return "formula";
        if (StringUtils.containsIgnoreCase(bookImgClz, "HNT")) return "hnt";
        if (id.matches("^P\\d+[-_]?.*")) return "default";
        if (id.matches("^T\\d+[-_]?.*")) return "table";
        // 根据宽，高确定图片类型
        String url = img.attr("src");
        BufferedImage sourceImg = null;
        InputStream murl = null;
        try {
            if (url.startsWith("http")) {
                murl = new URL(url).openStream();
                sourceImg = ImageIO.read(murl);
            } else if (url.startsWith("PICTUREURL/")) {
                File file = new File("E:\\renweiimage\\" + url);
                sourceImg = ImageIO.read(file);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (sourceImg == null) {
            System.out.println("【【发现未知图片】】" + url);
            return "default";
        }
        int h = sourceImg.getHeight();
        int w = sourceImg.getWidth();
        if (h <= 150 && w <= 200 && Math.abs(h - w) <= 6) {
            return "bz hnt";
        }
        if (h <= 150 && w <= 200) {
            return "hnt";
        }
        return "default";
    }
}
